// Generates a header with precalculated best dividers for PWMAudio at
// standard clock frequencies and audio rates.
// 

#include <stdint.h>
#include <stdio.h>
#include <math.h>

void find_pacer_fraction(int F_CPU, int target, uint16_t *numerator, uint16_t *denominator) {
    const uint16_t max = 0xFFFF;

    /*Cache last results so we dont have to recalculate*/
    static int last_target;
    static uint16_t bestNum;
    static uint16_t bestDenom;
    /*Check if we can load the previous values*/
    if (target == last_target) {
        *numerator = bestNum;
        *denominator = bestDenom;
        return;
    }

    float targetRatio = (float)F_CPU / target;
    float lowestError = 10000000;

    for (uint16_t denom = 1; denom < max; denom++) {
        uint16_t num = (int)((targetRatio * denom) + 0.5f); /*Calculate numerator, rounding to nearest integer*/

        /*Check if numerator is within bounds*/
        if (num > 0 && num < max) {
            float actualRatio = (float)num / denom;
            float error = fabsf(actualRatio - targetRatio);

            if (error < lowestError) {
                bestNum = num;
                bestDenom = denom;
                lowestError = error;
                if (error == 0) {
                    break;
                }
            }
        }
    }

    last_target = target;
    *numerator = bestNum;
    *denominator = bestDenom;
}

int main(int argc, char **argv) {
    (void) argc;
    (void) argv;
    int M = 1000000;
    int fsys[] = {50*M, 100*M, 120*M, 125*M, 128*M, 133*M, 150*M, 176*M, 200*M, 225*M, 240*M, 250*M, 276*M, 300*M};
    int freq[] = {8000, 11025, 16000, 22050, 32000, 44100, 48000, 88200, 96000, 176400, 192000};
    FILE *f = fopen("../libraries/PWMAudio/src/PWMAudioPrecalc.h", "w");
    fprintf(f, "// Generated by tools/makepacer.cpp, do not edit\n");
    fprintf(f, "typedef struct {\n");
    fprintf(f, "    uint32_t freq;\n");
    fprintf(f, "    uint16_t n;\n");
    fprintf(f, "    uint16_t d;\n");
    fprintf(f, "} PWMPacerPrecalc;\n");
    for (int i = 0; i < sizeof(fsys)/sizeof(fsys[0]); i++) {
        fprintf(f, "#%s F_CPU == %d\n", i == 0 ? "if" : "elif", fsys[i]);
        fprintf(f, "static const PWMPacerPrecalc __PWMAudio_pacer[] = {");
        for (int j = 0; j < sizeof(freq)/sizeof(freq[0]); j++) {
            uint16_t n, d;
            find_pacer_fraction(fsys[i], freq[j], &n, &d);
            fprintf(f, "{%d, %d, %d}", freq[j], n, d);
            if (j < sizeof(freq)/sizeof(freq[0]) - 1) {
                fprintf(f, ", ");
            }
        }
        fprintf(f, "};\n");
    }
    fprintf(f, "#else\n");
    fprintf(f, "const PWMPacerPrecalc __PWMAudio_pacer[] = {{1, 1, 1}}; // Invalid, should never match\n");
    fprintf(f, "#endif\n");
    fclose(f);
}

